package org.glassunit.core.deploy;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import org.glassunit.core.util.FileUtils;

/**
 * Directory based module.
 * @author Frederic Müller
 */
public class DirectoryModule extends Module {

    private final File root;

    /**
     * Creates a new directory module for the given directory.
     * The module's name is determined by the directory name
     * @param file directory
     */
    public DirectoryModule(final File file) {
        this(file.getName(), file);
    }

    /**
     * Creates a new directory module for the given directory with the given name.
     * @param name module name
     * @param file directory
     */
    public DirectoryModule(final String name, final File file) {
        super(name);
        if (file == null) {
            throw new NullPointerException("Missing file parameter");
        }
        if (!file.isDirectory()) {
            throw new RuntimeException(String.format("File '%s' is not a directory", file.getAbsolutePath()));
        }
        this.root = file;
    }

    @Override
    public Set<String> getFiles() {
        final Set<String> items = new HashSet<String>();
        items.addAll(getFiles(root));
        items.addAll(super.getFiles());

        return items;
    }

    /**
     * Retrieves all files within the given directory.
     * @param directory directory
     * @return list of files
     */
    private Set<String> getFiles(final File directory) {
        final Set<String> items = new HashSet<String>();
        for (File file : directory.listFiles()) {
            if (file.isDirectory()) {
                items.addAll(getFiles(file));
            } else {
                items.add(FileUtils.getRelativePath(root, file));
            }
        }
        return items;
    }

    @Override
    public Set<String> getDirectories() {
        final Set<String> items = new HashSet<String>();
        items.addAll(getDirectories(root));
        items.addAll(super.getDirectories());
        return items;
    }

    /**
     * Retrieves all directories within the given directory.
     * @param directory directory
     * @return list of directories
     */
    private Set<String> getDirectories(final File directory) {
        final Set<String> items = new HashSet<String>();
        for (File file : directory.listFiles()) {
            if (file.isDirectory()) {
                items.addAll(getDirectories(file));
                items.add(FileUtils.getRelativePath(root, file));
            }
        }
        return items;
    }

    @Override
    public InputStream getFile(final String filename) throws IOException {
        final File file = new File(root, filename);

        if (file.exists()) {
            return new BufferedInputStream(new FileInputStream(file));
        } else {
            return super.getFile(filename);
        }
    }

    @Override
    public Long getFileSize(final String filename) {
        final File file = new File(root, filename);

        if (file.exists()) {
            return file.length();
        } else {
            return super.getFileSize(filename);
        }
    }

    @Override
    public boolean exists(final String filename) {
        final File file = new File(root, filename);
        if (file.exists()) {
            return true;
        }
        return super.exists(filename);
    }
}
